import json
import redis
from typing import Any, Optional, Callable
from functools import wraps
from datetime import timedelta
from flask import current_app
from app.utils.log_config import get_logger

logger = get_logger('cache')

class RedisCache:
    """
    Redis缓存管理器，支持优雅降级
    """
    def __init__(self, host='localhost', port=6379, db=0, password=None):
        # 从应用配置获取Redis设置，如果在应用上下文中
        self.enabled = False
        try:
            if current_app and hasattr(current_app, 'config'):
                self.enabled = current_app.config.get('REDIS_ENABLED', False)
                self.host = current_app.config.get('REDIS_HOST', host)
                self.port = current_app.config.get('REDIS_PORT', port)
                self.db = current_app.config.get('REDIS_DB', db)
                self.password = current_app.config.get('REDIS_PASSWORD', password)
            else:
                # 不在应用上下文中时使用默认值
                self.host = host
                self.port = port
                self.db = db
                self.password = password
                # 默认禁用，避免在应用初始化前尝试连接
                self.enabled = False
        except Exception as e:
            logger.error(f"初始化Redis配置失败: {e}")
            self.enabled = False
        
        self._redis_client = None
        
        if not self.enabled:
            logger.info("Redis缓存已禁用，将使用内存缓存降级方案")
    
    @property
    def redis_client(self):
        """
        获取Redis客户端实例（懒加载）
        """
        # 如果Redis被禁用，直接返回None
        if not self.enabled:
            return None
            
        if self._redis_client is None:
            try:
                self._redis_client = redis.Redis(
                    host=self.host,
                    port=self.port,
                    db=self.db,
                    password=self.password,
                    decode_responses=True
                )
                # 测试连接
                self._redis_client.ping()
                logger.info("Redis连接成功")
            except Exception as e:
                logger.error(f"Redis连接失败: {e}")
                self._redis_client = None
        return self._redis_client
    
    def get(self, key: str) -> Optional[Any]:
        """
        从缓存获取数据
        
        Args:
            key: 缓存键
            
        Returns:
            缓存的数据，如果不存在或出错则返回None
        """
        try:
            client = self.redis_client
            if client is None:
                return None
            
            value = client.get(key)
            if value is not None:
                return json.loads(value)
            return None
        except Exception as e:
            logger.error(f"获取缓存失败 [{key}]: {e}")
            return None
    
    def set(self, key: str, value: Any, expire: Optional[int] = None) -> bool:
        """
        设置缓存
        
        Args:
            key: 缓存键
            value: 要缓存的值（会被序列化为JSON）
            expire: 过期时间（秒）
            
        Returns:
            是否设置成功
        """
        try:
            client = self.redis_client
            if client is None:
                return False
            
            json_value = json.dumps(value, ensure_ascii=False, default=str)
            if expire:
                return client.setex(key, expire, json_value)
            else:
                return client.set(key, json_value)
        except Exception as e:
            logger.error(f"设置缓存失败 [{key}]: {e}")
            return False
    
    def delete(self, key: str) -> bool:
        """
        删除缓存
        
        Args:
            key: 缓存键
            
        Returns:
            是否删除成功
        """
        try:
            client = self.redis_client
            if client is None:
                return False
            
            return bool(client.delete(key))
        except Exception as e:
            logger.error(f"删除缓存失败 [{key}]: {e}")
            return False
    
    def delete_pattern(self, pattern: str) -> int:
        """
        删除匹配模式的所有缓存
        
        Args:
            pattern: 键模式，如 'inventory:*'
            
        Returns:
            删除的键数量
        """
        try:
            client = self.redis_client
            if client is None:
                return 0
            
            keys = client.keys(pattern)
            if keys:
                return client.delete(*keys)
            return 0
        except Exception as e:
            logger.error(f"批量删除缓存失败 [{pattern}]: {e}")
            return 0
    
    def exists(self, key: str) -> bool:
        """
        检查缓存是否存在
        
        Args:
            key: 缓存键
            
        Returns:
            是否存在
        """
        try:
            client = self.redis_client
            if client is None:
                return False
            
            return bool(client.exists(key))
        except Exception as e:
            logger.error(f"检查缓存存在性失败 [{key}]: {e}")
            return False
    
    def clear(self) -> bool:
        """
        清空当前数据库的所有缓存
        
        Returns:
            是否清空成功
        """
        try:
            client = self.redis_client
            if client is None:
                return False
            
            client.flushdb()
            logger.info("缓存已清空")
            return True
        except Exception as e:
            logger.error(f"清空缓存失败: {e}")
            return False

# 创建全局缓存实例
cache = RedisCache()

def cached(key_pattern: str, expire: int = 300):
    """
    缓存装饰器
    
    Args:
        key_pattern: 键模式，可以包含参数占位符，如 'inventory:{product_id}'
        expire: 过期时间（秒）
        
    Returns:
        装饰器函数
    """
    def decorator(f: Callable) -> Callable:
        @wraps(f)
        def wrapper(*args, **kwargs):
            # 生成缓存键
            try:
                key = key_pattern.format(**kwargs)
            except KeyError:
                # 如果参数不匹配，使用函数名和参数的哈希值
                import hashlib
                arg_str = str(args) + str(sorted(kwargs.items()))
                hash_value = hashlib.md5(arg_str.encode()).hexdigest()
                key = f"{f.__name__}:{hash_value}"
            
            # 尝试从缓存获取
            cached_result = cache.get(key)
            if cached_result is not None:
                logger.debug(f"缓存命中: {key}")
                return cached_result
            
            # 执行原函数
            result = f(*args, **kwargs)
            
            # 存入缓存
            cache.set(key, result, expire)
            logger.debug(f"缓存设置: {key}, 过期时间: {expire}s")
            
            return result
        
        return wrapper
    
    return decorator

# 缓存失效装饰器
def invalidate_cache(pattern: str):
    """
    缓存失效装饰器，在函数执行后使匹配模式的缓存失效
    
    Args:
        pattern: 要失效的缓存键模式
        
    Returns:
        装饰器函数
    """
    def decorator(f: Callable) -> Callable:
        @wraps(f)
        def wrapper(*args, **kwargs):
            # 执行原函数
            result = f(*args, **kwargs)
            
            # 使缓存失效
            count = cache.delete_pattern(pattern)
            logger.debug(f"缓存失效: {pattern}, 失效数量: {count}")
            
            return result
        
        return wrapper
    
    return decorator

# 上下文管理器，用于临时禁用缓存
class no_cache:
    """
    临时禁用缓存的上下文管理器
    """
    def __init__(self):
        self.original_client = cache._redis_client
    
    def __enter__(self):
        cache._redis_client = None
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        cache._redis_client = self.original_client